package xstring

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"gitee.com/xiaoyutab/xgotool/xerror"
)

// 对字符串进行base64编码
//
//	p	待编码的字符串
func Base64(p string) string {
	return BaseByte64([]byte(p))
}

// 对字节码进行Base64编码
//
//	p	待编码的字节码
func BaseByte64(p []byte) string {
	return base64.StdEncoding.EncodeToString(p)
}

// 对字符串进行encode64解码
//
//	p	待解码的字符串
func UnBase64(p string) string {
	return string(UnBase64Byte(p))
}

// 对字符串进行encode64解码
//
//	p	待解码的字符串
func UnBase64Byte(p string) []byte {
	for i := 0; i < 4; i++ {
		// 因Base64后面容易出现=，而部分程序会自动去除=，所以此处需要使用for来识别、判断=
		uDec, err := base64.StdEncoding.DecodeString(p)
		if err == nil {
			// URLBase
			return uDec
		}
		uDec, err = base64.URLEncoding.DecodeString(p)
		if err == nil {
			return uDec
		}
		p += "="
	}
	return []byte{}
}

// 对字符串进行encode64解码[去除填充解码，一般用于jwt]
//
//	p	待解码的字符串
func UnBase64Raw(p string) string {
	return string(UnBase64Byte(p))
}

// 对字符串进行encode64解码[去除填充解码，一般用于jwt]
//
//	p	待解码的字符串
func UnBase64RawByte(p string) []byte {
	for i := 0; i < 4; i++ {
		// 因Base64后面容易出现=，而部分程序会自动去除=，所以此处需要使用for来识别、判断=
		uDec, err := base64.RawStdEncoding.DecodeString(p)
		if err == nil {
			// URLBase
			return uDec
		}
		uDec, err = base64.RawURLEncoding.DecodeString(p)
		if err == nil {
			return uDec
		}
		p += "="
	}
	return []byte{}
}

// 对字符串进行base64编码[去除填充吗，一般用于jwt]
//
//	p	待编码的字符串
func Base64Raw(p string) string {
	return BaseByte64Raw([]byte(p))
}

// 对字符串进行base64编码[去除填充码，一般用于jwt]
//
//	p	待编码的字符串
func BaseByte64Raw(p []byte) string {
	return base64.RawURLEncoding.EncodeToString(p)
}

// 计算字符串的MD5值
//
//	text	待计算的字符串值
func MD5(text string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(text)))
}

// 计算byte字节流的MD5值
//
//	data	待计算的字节流的值
func MD5Byte(data []byte) string {
	return fmt.Sprintf("%x", md5.Sum(data))
}

// 计算文件的MD5值
//
//	file	文件的绝对路径
func MD5File(file string) string {
	data, err := os.ReadFile(file)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%x", md5.Sum(data))
}

// 计算字符串的SHA1值
func SHA1(text string) string {
	return fmt.Sprintf("%x", sha1.Sum([]byte(text)))
}

// 计算byte字节流的SHA1值
//
//	data	待计算的字节流
func SHA1Byte(data []byte) string {
	return fmt.Sprintf("%x", sha1.Sum(data))
}

// 计算文件的SHA1
//
//	file	文件的绝对路径
func SHA1File(file string) string {
	data, err := os.ReadFile(file)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

// JWTToken 无视秘钥进行解密
//
//	token	JWT token结构体
//	stu		JWT token解析出的数据
func UnJwtNoKey(token string, stu any) error {
	bodys := strings.Split(token, ".")
	if len(bodys) != 3 {
		return xerror.New("JWT格式不正确")
	}
	// base64解码jwt体
	dec := UnBase64Raw(bodys[1])
	if dec == "" {
		// 使用去除填充码形式解码
		tem := UnBase64(bodys[1])
		if tem == "" {
			return xerror.New("Base64解码失败")
		}
		dec = tem
	}
	fmt.Println("jsons: ", dec)
	err := json.Unmarshal([]byte(dec), stu)
	if err != nil {
		return err
	}
	return nil
}

// 生成jwt签名
//
//	key			签名
//	payloadData	待加密的内容
func Jwt(key []byte, payloadData any) (string, error) {
	// 标准头部
	header := `{"alg":"HS256","typ":"JWT"}`
	// 将负载的数据转换为json
	payload, jsonErr := json.Marshal(payloadData)
	if jsonErr != nil {
		return "", fmt.Errorf("负载json解析错误")
	}
	// 将头部和负载通过base64编码，并使用.作为分隔进行连接
	encodedHeader := Base64Raw(header)
	encodedPayload := Base64Raw(string(payload))
	HeaderAndPayload := encodedHeader + "." + encodedPayload
	// 使用签名使用的key将传入的头部和负载连接所得的数据进行签名
	signature, err := generateSignature(key, []byte(HeaderAndPayload))
	if err != nil {
		return "", err
	}
	// 将token的三个部分使用.进行连接并返回
	return HeaderAndPayload + "." + signature, nil
}

// 带签名秘钥的JWT解密
//
//	key		签名秘钥
//	token	JWT字符串
//	stu		结构体形式的参数体
func UnJwt(key []byte, token string, stu any) error {
	// 分解规定，我们使用.进行分隔，所以我们通过.进行分隔成三个字符串的数组
	jwtParts := strings.Split(token, ".")
	// 数据数组长度不是3就说明token在格式上就不合法
	if len(jwtParts) != 3 {
		return xerror.New("非法token")
	}
	// 分别拿出
	encodedHeader := jwtParts[0]
	encodedPayload := jwtParts[1]
	signature := jwtParts[2]
	// 使用key将token中的头部和负载用.连接后进行签名
	// 这个签名应该个token中第三部分的签名一致
	confirmSignature, err := generateSignature(key, []byte(encodedHeader+"."+encodedPayload))
	if err != nil {
		return xerror.New("生成签名错误")
	}
	// 如果不一致
	if signature != confirmSignature {
		return xerror.New("token验证失败")
	}
	err = json.Unmarshal([]byte(UnBase64Raw(encodedPayload)), stu)
	if err != nil {
		return err
	}
	return nil
}

// JWT签名方法
//
//	key		签名秘钥
//	data	签名内容
func generateSignature(key []byte, data []byte) (string, error) {
	// 创建一个哈希对象
	hash := hmac.New(sha256.New, key)
	// 将要签名的信息写入哈希对象中 hash.Write(data)
	_, err := hash.Write(data)
	if err != nil {
		return "", err
	}
	// hash.Sum()计算签名，在这里会返回签名内容
	// 将签名经过base64编码生成字符串形式返回。
	return Base64Raw(string(hash.Sum(nil))), nil
}
